%%%-------------------------------------------------------------------
%%% File    : di_sup.erl
%%% Author  :  <whhbest>
%%% Description : 
%%%
%%% Created : 16 Mar 2011 by  <>
%%%-------------------------------------------------------------------
-module(dis_sup).

-behaviour(gen_server).

-export([start_link/0]).

-export([add_sup/1, del_sup/1, whereis_sup/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	terminate/2, code_change/3]).

-define(SERVER, ?MODULE).

-record(sup, {master_sup,
	     slave_sup}).

%%------
start_link() ->
    gen_server:start_link({global, ?SERVER}, ?MODULE, [], []).

add_sup(Node) when is_atom(Node) ->
    gen_server:call({global, ?SERVER}, {add_sup, Node});
add_sup(_) ->
    {error, badarg}.

del_sup(Node) when is_atom(Node) ->
    gen_server:call({global, ?SERVER}, {del_sup, Node});
del_sup(_) ->
    {error, badarg}.

whereis_sup() ->
    gen_server:call({global, ?SERVER}, whereis_sup).

%%-----callbacks
init([]) ->
    process_flag(trap_exit, true),
    State = #sup{master_sup=node(),
		slave_sup=[]},%%[{node, Pid}]
    {ok, State}.

handle_call(whereis_sup, _From, S=#sup{master_sup=Msup,
				      slave_sup=Slave}) ->
    Reply =[{master_sup, Msup},{slave_sup, Slave}],
    {reply, Reply, S};

handle_call({Op, Node}, _From, S=#sup{master_sup=Msup}) ->
    {Res, Ns} = case Node =:= Msup of
		    true ->
			{{error, noop}, S};
		    false ->
			op_sup(Op, Node, S)
                end,
    {reply, Res, Ns};

handle_call(_Req, _From, S) ->
    {reply, ok, S}.

handle_cast({redirect, Node, Pid}, S=#sup{slave_sup=Sup}) ->
    {noreply, S#sup{slave_sup=[{Node, Pid}|Sup]}};
handle_cast(_Msg, S) ->
    {noreply, S}.

handle_info({'EXIT', _, normal}, S) ->
    {noreply, S};
handle_info({'EXIT', Pid, noconnection}, S=#sup{slave_sup=Slave}) ->
    {noreply, S#sup{slave_sup=lists:keydelete(Pid, 2, Slave)}};
handle_info({'EXIT', Pid, _Reason}, S) ->
    Ns = do_restart(Pid, S),
    {noreply, Ns};
handle_info(_Info, S) ->
    {noreply, S}.

terminate(_Reason, _S) ->
   
    ok.

code_change(_Oldvsn, S, _Extra) ->
    {ok, S}.

%%-------internal
op_sup(add_sup, Node, S=#sup{slave_sup=Slave}) ->
   case lists:keymember(Node, 1, Slave) of
       true ->
	   {{error, exist}, S};
       false ->
	   do_add(Node, S)
   end;
op_sup(del_sup, Node, S=#sup{slave_sup=Slave}) ->
    case lists:keymember(Node, 1, Slave) of
	fasle ->
	    {{error, noexist}, S};
	true ->
	    do_del(Node, S)
    end.

do_add(Node, S=#sup{slave_sup=Sup}) ->
    case net_adm:ping(Node) of
	pang ->
	    {{error, unconnected}, S};
	pong ->
	    Res = rpc:call(Node, slave_sup, start, [self()]),
	    case Res of
		{ok ,Pid} ->
		    link(Pid),
		    {ok, S#sup{slave_sup=[{Node, Pid}|Sup]}};
		{badrpc, Reason} ->
		    {{error, Reason}, S}
            end
    end.

do_del(Node, S=#sup{slave_sup=Sup}) ->
    case lists:keysearch(Node, 1, Sup) of
	{value, {Node, Pid}} ->
	    gen_server:cast(Pid, {del, self()}),
	    {ok, S#sup{slave_sup=lists:keydelete(Node, 1, Sup)}};
	false ->
	    {{error, noexist}, S}
    end.

do_restart(Pid, S=#sup{slave_sup=Sup}) ->
    case lists:keysearch(Pid, 2, Sup) of
	{value, {Node, Pid}} ->
	    Res = rpc:call(Node, slave_sup, start, [self()]),
	    case Res of
		{ok, Npid} ->
		    S#sup{slave_sup=[{Node, Npid}|lists:keydelete(Pid, 2, Sup)]};
		{badrpc, _Reason} ->
		    S#sup{slave_sup=lists:keydelete(Pid, 2, Sup)}
            end;
	false ->
	    S
    end.

%%do_exit([]) ->
%%    ok;
%%do_exit([{_, Pid}|T]) ->
 %%5   gen_server:cast(Pid, {do_exit, self()}),
 %%   do_exit(T).
